package plp.functional1.memory;

import java.util.HashMap;
import java.util.Stack;

import plp.expressions2.expression.Id;
import plp.expressions2.expression.Valor;
import plp.expressions2.memory.Contexto;
import plp.expressions2.memory.ContextoExecucao;
import plp.expressions2.memory.VariavelJaDeclaradaException;
import plp.expressions2.memory.VariavelNaoDeclaradaException;
import plp.functional1.util.ValorFuncao;

public class ContextoExecucaoFuncional
	implements AmbienteExecucaoFuncional {

	/**
	 * A pilha de blocos de funcao deste contexto.
	 */
	private Stack<HashMap<Id, ValorFuncao>> pilhaFuncao;
	private ContextoExecucao pilhaIdValor;
	private Contexto<ValorFuncao> pilhaIdValorFunc;

	/**
	 * Construtor da classe.
	 */
	public ContextoExecucaoFuncional() {
		this.pilhaFuncao = new Stack<HashMap<Id, ValorFuncao>>();
		this.pilhaIdValor = new ContextoExecucao();
		this.pilhaIdValorFunc = new Contexto<ValorFuncao>();
		this.pilhaIdValorFunc.setPilha(this.pilhaFuncao);
	}

	public void incrementa() {
		this.pilhaIdValor.incrementa();
		pilhaFuncao.push(new HashMap<Id, ValorFuncao>());
	}

	public void restaura() {
		this.pilhaIdValor.restaura();
		pilhaFuncao.pop();
	}

	/**
	 * Mapeia um identificador em uma funcao.
	 *
	 * @param idArg o identificador
	 * @param funcao a funcao.
	 * @exception VariavelJaDeclaradaException se o id ja' estiver declarado.
	 */
	public void mapFuncao(Id idArg, ValorFuncao funcao)
		throws VariavelJaDeclaradaException {
        this.pilhaIdValorFunc.map(idArg, funcao);
	}

	/**
	 * Retorna uma funcao.
	 *
	 * @param idArg o identificador que mapeia a funcao
	 * @param funcao a funcao.
	 * @exception VariavelNaoDeclaradaException se o id nao estiver declarado.
	 */
	public ValorFuncao getFuncao(Id idArg)
		throws VariavelNaoDeclaradaException {
		return this.pilhaIdValorFunc.get(idArg);
	}

	public Valor get(Id idArg) throws VariavelNaoDeclaradaException {
		return this.pilhaIdValor.get(idArg);
	}

	public void map(Id idArg, Valor tipoId) throws VariavelJaDeclaradaException {
		this.pilhaIdValor.map(idArg, tipoId);
	}

}
